
package kuti.grp5;

import static java.lang.Math.PI;
import static java.lang.Math.pow;
import kuti.EKutiState;
import kuti.IChargingStation;
import kuti.IKuti;
import kuti.ISimulParams;

/**
 *
 * The enregy manager manages the energy for the kuti.
 * 
 * It removes and adds energy for the kuti, when it do action.
 * 
 * 
 * @author Marc Schorderet <marc.schorderet@edu.hefr.ch>
 * @author Vincenzo Do <vincenzo.do@edu.hefr.ch>
 * 
 */

public class EnergyManager {
    
    /**
     * The current kuti associated with the energy manager
     */
    
    private Kuti kuti;
    
    /**
     * The radius of the kuti
     */
    
    private double radius;
    
    /**
     * The energy bevor it stopped on the charging station
     */
    
    private double beginingEnergy;
    
    /**
     * The step number bevoir it stopped on the charging station
     */
    
    private double stopStepNumber;
    
    /**
     * The total area of the kuti
     */
    
    private double totalKutiArea;
    
    
    /**
     * True if the kuti is on a charging station, else false
     */
    
    private boolean isOnChargingStation;
    
    /**
     * Energy level 
     */
    private double energyLevel;
    
    /**
     * Simulation parameters
     */
    private ISimulParams simulParams;
    
    /**
     * Create the energy manager of a kuti.
     * 
     * @param kuti          Current kuti, to manage the energy
     * @param simulParams   Simulation parameters
     * @param initialEnergy Initial energy of the kuti
     * 
     */
    
    public EnergyManager(IKuti kuti, ISimulParams simulParams, double initialEnergy){
        
        // Check if the kuti is valid
        if(kuti == null){
            throw new IllegalArgumentException("The kuti is not defined.");
        }
        
        // Check if the simulation parameters are valid
        if(simulParams == null){
            throw new IllegalArgumentException("The simulation parameters are not defined.");
        }
        
        this.kuti = (Kuti) kuti;
        this.simulParams = simulParams;
        this.radius = simulParams.getKDiameter() / 2;
        this.beginingEnergy = initialEnergy;
        
        // Calculate the total area of the kuti
        this.totalKutiArea = radius * radius * PI;
        
    }
    
    /**
     * This method checks if the kuti has energy to moves.
     * 
     * @return Returns true, if the kuti has energy to moves, else return false. 
     * 
     */
    
    public boolean hasEnergyToMove(){
        return energyLevel >= simulParams.getKMovingEnergy();
    }
    
    /**
     * This method checks if the kuti has energy to rotate.
     * 
     * @return Returns true, if the kuti has energy to rotates, else return false. 
     * 
     */
    
    public boolean hasEnergyToRotate(){
        return energyLevel >= simulParams.getKRotationEnergy();
    }
    
    /**
     * This method checks if the kuti has energy to use radar.
     * 
     * @return Returns true, if the kuti has energy to use radar, else return false. 
     * 
     */
    
    public boolean hasEnergyToUseRadar(){
        return energyLevel >= simulParams.getRadarEnergy();
    }
    
    /**
     * This method checks if the kuti has energy to cut.
     * 
     * @return Returns true, if the kuti has energy to cut, else return false. 
     * 
     */
    
    public boolean hasEnergyToCut(){
        return energyLevel >= simulParams.getKCutterEnergy();
    }
    
    /**
     * Returns the current energy level of the kuti.
     * 
     * @return Returns the energy level of the kuti.
     * 
     */
    
    public double getEnergyLevel(){
        return energyLevel;
    }
    
    /**
     * This method set the energy on the specified energy.
     * 
     * @param energy    The energy to set.
     * 
     */
    
    public void equilibrate(double energy){
        energyLevel = energy;
    }
    
    /**
     * This method substracte the specified energy to the current
     * energy level.
     * 
     * @param energy    Energy to remove
     * 
     * if the energy to remeves is bigger that the current energy,
     * the energy level is set to 0.0.
     * 
     */
    
    public void consumeEnergy(double energy){
        this.energyLevel -= energy;
        if(this.energyLevel < 0.0)
            this.energyLevel = 0.0;
    }
    
    /**
     * This method add the specified energy to the current energy.
     * 
     * @param energy    Energy to add to the current energy level.
     * 
     * if the end energy is bigger that the maximal energy specified
     * in the simulation parameters, the energy level is set to max
     * energy level.
     * 
     */
    
    public void chargeEnergy(double energy){
        this.energyLevel += energy;
        if(this.energyLevel > simulParams.getKMaxEnergy())
            this.energyLevel = simulParams.getKMaxEnergy();
    }
    
    /**
     * This method removes the moving energy.
     * 
     */
    
    public void removeMovingEnergy(){
        
        double energy = simulParams.getKMovingEnergy();
        consumeEnergy(energy);
        
    }
    
    /**
     * This method removes the rotation energy.
     * 
     * If the kuti is on the charging station, the energy don't
     * change.
     * 
     */
    
    public void removeRotationEnergy(){
        
        if(!isOnChargingStation){
            double energy = simulParams.getKRotationEnergy();
            consumeEnergy(energy);
        }
        
    }
    
    /**
     * This method removes the radar energy.
     * 
     * If the kuti is on the charging station, the energy don't
     * change.
     * 
     */
    
    public void removeRadarEnergy(){
        
        if(!isOnChargingStation){
            double energy = simulParams.getRadarEnergy();
            consumeEnergy(energy);
        }
        
    }
    
    /**
     * This method removes the cutting energy.
     * 
     */
    
    public void removeCutterEnergy(){
        
        double energy = simulParams.getKCutterEnergy();
        consumeEnergy(energy);
        
    }
    
    /**
     * This method removes the magnetic obstacle liberation energy.
     * 
     */
    
    public void removeMagneticObstacleLiberationEnergy(){
        
        double energy = simulParams.getMagnetLiberationEnergy();
        consumeEnergy(energy);
               
    }
    
    /**
     * This method update the energy info.
     * 
     * It charge the kuti, if it is on a charging station.
     * 
     */
    
    public void updateEnergyInfo(){
        
        isOnChargingStation = isOnChargingStation();
        
        if(kuti.getState() == EKutiState.MOVING){
            stopStepNumber = kuti.getWorldManager().getStepNr();
            beginingEnergy = kuti.getEnergyLevel();
        }else{
            if(isOnChargingStation){
                charging();
            }
        }
        
    }
    
    /**
     * This method charging the kuti.
     * 
     * It check if the kuti is on a charging station and charging it.
     * 
     */
    
    private void charging(){
        
        // For all charging station
        for(IChargingStation ics : kuti.getWorldManager().getAllChargingStation()){
            
            ChargingStation cs = (ChargingStation) ics;
            
            // Check if the kuti is on the charging station
            if(cs.isKutiOnChargingStation(kuti, radius)){
                
                // Calculate the commum area
                double commonArea = cs.commonAreaChargingStationKuti(kuti, radius);
                
                // Get the energy to add on the charging station
                double energy = getEnergy(cs, (totalKutiArea/commonArea));
                
                // charge the energy
                chargeEnergy(energy);
            
            }
        }
        
    }
    
    /**
     * This method check if a kuti is on a charging station.
     * 
     * @return Returns true if a kuti is on a charging station, else returns false.
     */
    
    private boolean isOnChargingStation(){
        
        // For all charging station
        for(IChargingStation ics : kuti.getWorldManager().getAllChargingStation()){

            ChargingStation cs = (ChargingStation) ics;
            
            // Check if the kuti is on the charging station
            if(cs.isKutiOnChargingStation(kuti, radius)){
                return true;
            }
            
        }
        
        return false;
        
    }
    
    
    /**
     * This method calculates the energy that the charging station can transmit 
     * to the kuti. The function is not linear. It depends on the energy of kuti
     * when it starts loading and the loading time.
     * 
     * @param cs        The current charging station
     * @param factor    The common area of the kuti and the charging station
     *                  divided by the total kuti area
     * 
     * @return Returns the energy the the charging station transmit to the kuti
     * 
     */
    
    public double getEnergy(IChargingStation cs, double factor){
        
        // Check if the charging station is valid.
        if(cs == null){
            throw new IllegalArgumentException("The charging station is not defined.");
        }
        
        // Check if the factor is valid
        if(Double.isNaN(factor)){
            throw new IllegalArgumentException("The factor is not a number");
        }
        
        // Energie of the kuti by begining loading
        double e0 = beginingEnergy;
        
        // The current load time of the kuti
        double t = kuti.getWorldManager().getStepNr() - stopStepNumber  - 1;
        
        // The maximal energy of the kuti
        double eMax = simulParams.getKMaxEnergy();
        
        return getEnergy(eMax, e0, cs.getTau(), factor, t);
        
    }
    
    /**
     * Calculate the additional energy with the specified parameters.
     * 
     * @param eMax      Max energy
     * @param e0        Begin energy
     * @param tau       Tau of the charging station
     * @param factor    Factor ( totalKutiArea / common area )
     * @param t         Charging time
     * 
     * @return Returns the energy to add to the kuti
     */
    
    private static double getEnergy(double eMax, double e0, double tau, double factor, double t){
        
        // Check if the max energy is valid
        if(Double.isNaN(eMax)){
            throw new IllegalArgumentException("The max energy is not a number");
        }
        
        // Check if the max energy is valid
        if(Double.isNaN(e0)){
            throw new IllegalArgumentException("The begin energy is not a number");
        }
        
        // Check if the max energy is valid
        if(Double.isNaN(tau)){
            throw new IllegalArgumentException("The tau is not a number");
        }
        
        // Check if the max energy is valid
        if(Double.isNaN(factor)){
            throw new IllegalArgumentException("The factor is not a number");
        }
        
        // Check if the max energy is valid
        if(Double.isNaN(t)){
            throw new IllegalArgumentException("The the time is not a number");
        }
        
        // Check if the e0 is smaller or same that eMax
        if(eMax < e0){
            throw new IllegalArgumentException("The max energy can't be smaller that the begin energy");
        }
        
        // Check if the factor is valid
        if(factor < 1.0){
            throw new IllegalArgumentException("The factor can't be smaller that 1.0");
        }
        
        // Check if the tau range is valid
        //if(tau < 0.0 || tau > 100){
        //    throw new IllegalArgumentException("The tau must be a value between 0 and 100 ");
        //}
        
        if(t < 0.0){
            throw new IllegalArgumentException("The time muste be positif");
        }
        
        return (eMax - e0) * (1 - pow(Math.E, -t/(factor * tau)));
        
    }
    
}
